package cim144.ctdp_array16_shift8_relu

import Chisel._
import chisel3.{RegInit, UInt, Vec, Wire, WireInit, when}
import chisel3.util.{Enum, RegEnable, log2Ceil}
import freechips.rocketchip.config._

class CIMdtp2ctlIO extends Bundle{
  val push_done     = Output(Bool())
  val save_done     = Output(Bool())
  val mvm_done      = Output(Bool())
  val mem_req_valid = Output(Bool())
  val mem_req_data  = Bits(OUTPUT,64)
  val baseAddr      = Output(UInt(64.W))
}

class CIMDatapath (implicit p: Parameters) extends Module with SystemConfig{
  val io = IO{new Bundle{
    val ctl = Flipped(new CIMctl2dtpIO)
    val dtp = new CIMdtp2ctlIO
    }
  }
  //***************1.config parameter************
  //rs1: |31*******25|24      |       23 |22********16|15**********8|7*********0|
  //     |           |isrelu  |bit_random|**wise_num**|**out_shift**|**adc_range|
  val con_para = Wire(new config_para)
  con_para.adc_range  := RegEnable(io.ctl.rs1(7 , 0),0.U, io.ctl.custom_config)
  con_para.out_shift  := RegEnable(io.ctl.rs1(15, 8),0.U, io.ctl.custom_config)
  con_para.wise_num   := RegEnable(io.ctl.rs1(22,16),0.U, io.ctl.custom_config)
  con_para.bit_random := RegEnable(io.ctl.rs1(23),false.B,io.ctl.custom_config)
  con_para.isrelu     := RegEnable(io.ctl.rs1(24),false.B,io.ctl.custom_config)

  //**************2.push****************
  // rs2: *********|21********11|10********0|
  //      *********|start_index*|*push_num**|
  //      start_index and push_num should be Multiples of 8
  // rs1: |63****************memory addr***0|

  val push_save_buf_cnt   = RegInit(0.U(log2Ceil(PUSH_MAX_SIZE+1).W))   // 7bit
  val baseAddr            = RegInit(0.U(CIM_XLEN.W))
  val push_enable = WireInit(false.B)
  val cim_done = WireInit(false.B)
  val u_pushbuffer = pushbuffer_shift(
    custom_push = io.ctl.custom_push,
    custom_mvm  = io.ctl.custom_mvm,
    custom_save = io.ctl.custom_save,
    ismvm       = io.ctl.ismvm,
    ispush      = io.ctl.ispush,
    push_enable = push_enable,
    push_index  = push_save_buf_cnt,
    cim_done    = cim_done ,
    mem_valid   = io.ctl.mem_valid,
    mem_data    = io.ctl.mem_data ,
    bit_random  = con_para.bit_random,
    wise_num    = con_para.wise_num,
    r1          = io.ctl.rs1,
    r2          = io.ctl.rs2
  )
  io.dtp.push_done  := u_pushbuffer._1 & io.ctl.ispush & io.ctl.mem_valid
  io.dtp.save_done  := u_pushbuffer._1 & io.ctl.issave & io.ctl.mem_req_ready
  val push_576      = u_pushbuffer._2
  val bitwise_end   = u_pushbuffer._3
  //*****************3.mvm***********************
  val u_mvmcim = mvmcim_shift(
      custom_mvm  =  io.ctl.custom_mvm,
      ismvm       =  io.ctl.ismvm,
      bitwise_end =  bitwise_end,
      adc_range   =  con_para.adc_range,
      out_shift   =  con_para.out_shift,
      isrelu      =  con_para.isrelu,
      push_576    =  push_576,
      r1          =  io.ctl.rs1,
      r2          =  io.ctl.rs2
  )
  cim_done        := u_mvmcim._1
  io.dtp.mvm_done := u_mvmcim._2
  val save_16x64   = (u_mvmcim._3).asTypeOf(Vec(SAVE_MAX_SIZE,UInt(CIM_XLEN.W)))
  //******************4.mem control ***************
  val mem_idle :: mem_op :: Nil= Enum(2)
  val mem_state = RegInit(mem_idle)
  push_enable := (mem_state===mem_op && io.ctl.ispush && io.ctl.mem_valid)

  io.dtp.mem_req_valid := (io.ctl.ispush || io.ctl.issave) && (mem_state===mem_idle) && !(io.dtp.push_done)
  when(io.ctl.isidle){
    mem_state := mem_idle
//  }elsewhen(memReq.fire()){
  }.elsewhen(io.dtp.mem_req_valid && io.ctl.mem_req_ready){
    mem_state := mem_op
  }
  io.dtp.mem_req_data := save_16x64(push_save_buf_cnt)

  when(io.ctl.custom_push|io.ctl.custom_save){
    push_save_buf_cnt   := io.ctl.rs2(21,14)
    baseAddr            := io.ctl.rs1
  }
  when(push_enable){
    baseAddr            := baseAddr + 8.U
    push_save_buf_cnt   := push_save_buf_cnt + 1.U
    mem_state           := mem_idle
  }
  when(mem_state===mem_op && io.ctl.issave){
    baseAddr            := baseAddr + 8.U
    push_save_buf_cnt   := push_save_buf_cnt + 1.U
    mem_state := mem_idle
  }
  io.dtp.baseAddr := baseAddr
}
